import {
    imgToDataUrl,
    downloadFile,
    readBlob,
    removeHTMLEntities,
    resizeImgSize,
    handleSelfCloseTags,
    addXmlns,
} from '../utils';
import { SVG } from '@svgdotjs/svg.js';
import drawBackgroundImageToCanvas from '../utils/simulateCSSBackgroundInCanvas';
import { transformToMarkdown } from '../parse/toMarkdown';
import { ERROR_TYPES } from '../constants/constant';
import { transformToTxt } from '../parse/toTxt';

//  导出插件
class Export {
    //  构造函数
    constructor(opt) {
        this.mindMap = opt.mindMap;
    }

    //  导出
    async export(type, isDownload = true, name = '思维导图', ...args) {
        if (this[type]) {
            const result = await this[type](name, ...args);
            if (isDownload) {
                downloadFile(result, name + '.' + type);
            }
            return result;
        } else {
            return null;
        }
    }

    // 创建图片url转换任务
    createTransformImgTaskList(svg, tagName, propName, getUrlFn) {
        const imageList = svg.find(tagName);
        return imageList.map(async (item) => {
            const imgUlr = getUrlFn(item);
            // 已经是data:URL形式不用转换
            if (/^data:/.test(imgUlr) || imgUlr === 'none') {
                return;
            }
            const imgData = await imgToDataUrl(imgUlr);
            item.attr(propName, imgData);
        });
    }

    //  获取svg数据
    async getSvgData(node) {
        let {
            exportPaddingX,
            exportPaddingY,
            errorHandler,
            resetCss,
            addContentToHeader,
            addContentToFooter,
            handleBeingExportSvg,
        } = this.mindMap.opt;
        let { svg, svgHTML, clipData } = this.mindMap.getSvgData({
            paddingX: exportPaddingX,
            paddingY: exportPaddingY,
            addContentToHeader,
            addContentToFooter,
            node,
        });
        if (clipData) {
            clipData.paddingX = exportPaddingX;
            clipData.paddingY = exportPaddingY;
        }
        let svgIsChange = false;
        // svg的image标签，把图片的url转换成data:url类型，否则导出会丢失图片
        const task1 = this.createTransformImgTaskList(
            svg,
            'image',
            'href',
            (item) => {
                return item.attr('href') || item.attr('xlink:href');
            },
        );
        // html的img标签
        const task2 = this.createTransformImgTaskList(
            svg,
            'img',
            'src',
            (item) => {
                return item.attr('src');
            },
        );
        const taskList = [...task1, ...task2];
        try {
            await Promise.all(taskList);
        } catch (error) {
            errorHandler(ERROR_TYPES.EXPORT_LOAD_IMAGE_ERROR, error);
        }
        // 开启了节点富文本编辑，需要增加一些样式
        if (this.mindMap.richText) {
            const foreignObjectList = svg.find('foreignObject');
            if (foreignObjectList.length > 0) {
                foreignObjectList[0].add(SVG(`<style>${resetCss}</style>`));
                svgIsChange = true;
            }
            // 如果还开启了数学公式，还要插入katex库的样式
            if (this.mindMap.formula) {
                const formulaList = svg.find('.ql-formula');
                if (formulaList.length > 0) {
                    const styleText = this.mindMap.formula.getStyleText();
                    if (styleText) {
                        const styleEl = document.createElement('style');
                        styleEl.innerHTML = styleText;
                        addXmlns(styleEl);
                        foreignObjectList[0].add(styleEl);
                        svgIsChange = true;
                    }
                }
            }
        }
        // 自定义处理svg的方法
        if (typeof handleBeingExportSvg === 'function') {
            svgIsChange = true;
            svg = handleBeingExportSvg(svg);
        }
        // svg节点内容有变，需要重新获取html字符串
        if (taskList.length > 0 || svgIsChange) {
            svgHTML = svg.svg();
        }
        return {
            node: svg,
            str: svgHTML,
            clipData,
        };
    }

    //   svg转png
    svgToPng(svgSrc, transparent, clipData = null) {
        const { maxCanvasSize, minExportImgCanvasScale } = this.mindMap.opt;
        return new Promise((resolve, reject) => {
            const img = new Image();
            // 跨域图片需要添加这个属性，否则画布被污染了无法导出图片
            img.setAttribute('crossOrigin', 'anonymous');
            img.onload = async () => {
                try {
                    const canvas = document.createElement('canvas');
                    const dpr = Math.max(
                        window.devicePixelRatio,
                        minExportImgCanvasScale,
                    );
                    let imgWidth = img.width;
                    let imgHeight = img.height;
                    // 如果是裁减操作的话，那么需要手动添加内边距，及调整图片大小为实际的裁减区域的大小，不要忘了内边距哦
                    let paddingX = 0;
                    let paddingY = 0;
                    if (clipData) {
                        paddingX = clipData.paddingX;
                        paddingY = clipData.paddingY;
                        imgWidth = clipData.width + paddingX * 2;
                        imgHeight = clipData.height + paddingY * 2;
                    }
                    // 检查是否超出canvas支持的像素上限
                    // canvas大小需要乘以dpr
                    let canvasWidth = imgWidth * dpr;
                    let canvasHeight = imgHeight * dpr;
                    if (
                        canvasWidth > maxCanvasSize ||
                        canvasHeight > maxCanvasSize
                    ) {
                        let newWidth = null;
                        let newHeight = null;
                        if (canvasWidth > maxCanvasSize) {
                            // 如果宽度超出限制，那么调整为上限值
                            newWidth = maxCanvasSize;
                        } else if (canvasHeight > maxCanvasSize) {
                            // 高度同理
                            newHeight = maxCanvasSize;
                        }
                        // 计算缩放后的宽高
                        const res = resizeImgSize(
                            canvasWidth,
                            canvasHeight,
                            newWidth,
                            newHeight,
                        );
                        canvasWidth = res[0];
                        canvasHeight = res[1];
                    }
                    canvas.width = canvasWidth;
                    canvas.height = canvasHeight;
                    const styleWidth = canvasWidth / dpr;
                    const styleHeight = canvasHeight / dpr;
                    canvas.style.width = styleWidth + 'px';
                    canvas.style.height = styleHeight + 'px';
                    const ctx = canvas.getContext('2d');
                    ctx.scale(dpr, dpr);
                    // 绘制背景
                    if (!transparent) {
                        await this.drawBackgroundToCanvas(
                            ctx,
                            styleWidth,
                            styleHeight,
                        );
                    }
                    // 图片绘制到canvas里
                    // 如果有裁减数据，那么需要进行裁减
                    if (clipData) {
                        ctx.drawImage(
                            img,
                            clipData.left,
                            clipData.top,
                            clipData.width,
                            clipData.height,
                            paddingX,
                            paddingY,
                            clipData.width,
                            clipData.height,
                        );
                    } else {
                        ctx.drawImage(img, 0, 0, styleWidth, styleHeight);
                    }
                    resolve(canvas.toDataURL());
                } catch (error) {
                    reject(error);
                }
            };
            img.onerror = (e) => {
                reject(e);
            };
            img.src = svgSrc;
        });
    }

    //  在canvas上绘制思维导图背景
    drawBackgroundToCanvas(ctx, width, height) {
        return new Promise((resolve, reject) => {
            const {
                backgroundColor = '#fff',
                backgroundImage,
                backgroundRepeat = 'no-repeat',
                backgroundPosition = 'center center',
                backgroundSize = 'cover',
            } = this.mindMap.themeConfig;
            // 背景颜色
            ctx.save();
            ctx.rect(0, 0, width, height);
            ctx.fillStyle = backgroundColor;
            ctx.fill();
            ctx.restore();
            // 背景图片
            if (backgroundImage && backgroundImage !== 'none') {
                ctx.save();
                drawBackgroundImageToCanvas(
                    ctx,
                    width,
                    height,
                    backgroundImage,
                    {
                        backgroundRepeat,
                        backgroundPosition,
                        backgroundSize,
                    },
                    (err) => {
                        if (err) {
                            reject(err);
                        } else {
                            resolve();
                        }
                        ctx.restore();
                    },
                );
            } else {
                resolve();
            }
        });
    }

    //  在svg上绘制思维导图背景
    drawBackgroundToSvg(svg) {
        return new Promise(async (resolve) => {
            const {
                backgroundColor = '#fff',
                backgroundImage,
                backgroundRepeat = 'repeat',
            } = this.mindMap.themeConfig;
            // 背景颜色
            svg.css('background-color', backgroundColor);
            // 背景图片
            if (backgroundImage && backgroundImage !== 'none') {
                const imgDataUrl = await imgToDataUrl(backgroundImage);
                svg.css('background-image', `url(${imgDataUrl})`);
                svg.css('background-repeat', backgroundRepeat);
                resolve();
            } else {
                resolve();
            }
        });
    }

    //  导出为png
    /**
     * 方法1.把svg的图片都转化成data:url格式，再转换
     * 方法2.把svg的图片提取出来再挨个绘制到canvas里，最后一起转换
     */
    async png(name, transparent = false, node = null) {
        this.handleNodeExport(node);
        const { str, clipData } = await this.getSvgData(node);
        const svgUrl = await this.fixSvgStrAndToBlob(str);
        const res = await this.svgToPng(svgUrl, transparent, clipData);
        return res;
    }

    // 导出指定节点，如果该节点是激活状态，那么取消激活和隐藏展开收起按钮
    handleNodeExport(node) {
        if (node && node.getData('isActive')) {
            node.deactivate();
            const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt;
            if (
                !alwaysShowExpandBtn &&
                !notShowExpandBtn &&
                node.getData('expand')
            ) {
                node.removeExpandBtn();
            }
        }
    }

    //  导出为pdf
    async pdf(name, transparent = false) {
        if (!this.mindMap.doExportPDF) {
            throw new Error('请注册ExportPDF插件');
        }
        const img = await this.png(name, transparent);
        // 使用jspdf库
        // await this.mindMap.doExportPDF.pdf(name, img)
        // 使用pdf-lib库
        const res = await this.mindMap.doExportPDF.pdf(img);
        return res;
    }

    // 导出为xmind
    async xmind(name) {
        if (!this.mindMap.doExportXMind) {
            throw new Error('请注册ExportXMind插件');
        }
        const data = this.mindMap.getData();
        const blob = await this.mindMap.doExportXMind.xmind(data, name);
        const res = await readBlob(blob);
        return res;
    }

    //  导出为svg
    async svg(name) {
        const { node } = await this.getSvgData();
        node.first().before(SVG(`<title>${name}</title>`));
        await this.drawBackgroundToSvg(node);
        const str = node.svg();
        const res = await this.fixSvgStrAndToBlob(str);
        return res;
    }

    // 修复svg字符串，并且转换为blob数据
    async fixSvgStrAndToBlob(str) {
        // 移除字符串中的html实体
        str = removeHTMLEntities(str);
        // 给html自闭合标签添加闭合状态
        str = handleSelfCloseTags(str);
        // 转换成blob数据
        const blob = new Blob([str], {
            type: 'image/svg+xml',
        });
        const res = await readBlob(blob);
        return res;
    }

    //  导出为json
    async json(name, withConfig = true) {
        const data = this.mindMap.getData(withConfig);
        const str = JSON.stringify(data);
        const blob = new Blob([str]);
        const res = await readBlob(blob);
        return res;
    }

    //  专有文件，其实就是json文件
    async smm(name, withConfig) {
        const res = await this.json(name, withConfig);
        return res;
    }

    // markdown文件
    async md() {
        const data = this.mindMap.getData();
        const content = transformToMarkdown(data);
        const blob = new Blob([content]);
        const res = await readBlob(blob);
        return res;
    }

    // txt文件
    async txt() {
        const data = this.mindMap.getData();
        const content = transformToTxt(data);
        const blob = new Blob([content]);
        const res = await readBlob(blob);
        return res;
    }
}

Export.instanceName = 'doExport';

export default Export;
